//General Purpose
using System;
using System.Collections.Generic;
using System.Text;

//For the project
using CS6238Project2.Banking.Communication;
using CS6238Project2.Banking.Commerce;
using CS6238Project2.Banking.Tools;
using System.ComponentModel;
//For crypto
using System.Security.Cryptography.Xml;

//For Serialization
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace CS6238Project2.Banking.Entity
{
    /// <summary>
    /// A special user that takes the role of merchant
    /// </summary>
    [Serializable]
    public class Merchant : User
    {
        private BindingList<Good> inventory;

        public Merchant()
        {

            name = "";
            certificates = new BindingList<Certificate>();
            goods = new BindingList<Good>();
            inventory = new BindingList<Good>();
        }


        /// <summary>
        /// Processes an order received from a user or the bank
        /// </summary>
        /// <param name="received">The parcel that was received</param>
        /// <returns>A parcel to be sent</returns>
        new public Parcel ProcessParcel(Parcel received)
        {
            try
            {
                //Check to make sure receiver and itself are the same
                if (received.Receiver != name)
                {
                    received.MessageData = "Parcel receiver was " + received.Receiver +
                                        ", but should be " + name;
                    received.Type = "Invalid Parcel";
                }


                //CHECK
                if (received.Type == "Transaction Response")
                {
                    //Should ask for a list now
                    received = new Parcel();
                    received.Sender = name;
                    received.Receiver = "Bank of America";
                    received.Type = "List Request";
                    received.Number = account.Number;
                }
                //CHECK
                else if (received.Type == "Order Response")
                {
                    received = CompleteOrder(received);//process order completion
                    received.Type = "Terminated";
                }
                //CHECK
                else if (received.Type == "Order")
                {
                    received = DoubleTransaction(received);//create an order to be sent back
                    received.Type = "Dual Transaction";
                }
                //CHECK
                else if (received.Type == "Dual Transaction Response")
                {
                    //This is used when the bank approves a purchase transaction
                    received = CompleteUserOrder(received);
                    received.Type = "Order Response";
                }
                //CHECK
                else if (received.Type == "Registration Request")
                {
                    //asks to register with merchant
                    received = RegisterUser(received);
                    received.Type = "Registration Response";
                }
                //CHECK
                else if (received.Type == "Registration Response")
                {
                    //says whether the registration is approved
                    received = Register(received);//Inherited
                    received.Type = "Terminated";
                }
                else if (received.Type == "List Request")
                {
                    received.Receiver = received.Sender;
                    received.Sender = name;
                    received.InventoryList = inventory;
                    received.Type = "List Response";
                }
                else
                {
                    received.Type = "Terminated";
                    received.MessageData = "Type " + received.Type + " not recognized";
                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                received.MessageData = "Exception occurred.";
            }


            if (received.Receiver == name)
            {
                received.Receiver = received.Sender;
                received.Sender = name;
            }
            
            return received;
        }


        /// <summary>
        /// Verifies a proposed purchase is in stock and at the price quoted.
        /// </summary>
        /// <param name="good">The good to be verified</param>
        /// <returns>The index of that good</returns>
        private int VerifiedGood(Good good)
        {
            int index = -1;
            int counter = 0;
            foreach (Good g in inventory)
            {
                if ((g.Name == good.Name) && (g.Worth == good.Worth))
                {
                    index = counter;
                }
                counter++;
            }
            return index;
        }


        /// <summary>
        /// Handles the reception of an order from a user, as well as how to send that
        /// to the bank for approval
        /// </summary>
        /// <param name="received">The parcel that was received</param>
        /// <returns>A parcel to be sent off to the bank</returns>
        protected Parcel DoubleTransaction(Parcel parcel)
        {
            /*
             * Parcel =
             * {
             * Certificate (defines for bank how to take this apart)
             * merchant transaction (deposit)
             * customer transaction (withdraw)
             * Auth
             * 
             * }
             */
            Purchase purchase = new Purchase();
            Transaction userTrans = new Transaction();
            Transaction mercTrans = new Transaction();

            //Set the token of the certificate to that of the user who sent to decrypt
        
            parcel.Certificate.Token = RetrieveCertificate(parcel.Certificate.Subject, false).Token;

            //First retrieve the transaction and purchase.  Auth is irrelevant
            try
            {
                purchase = parcel.d_Purchase();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
            //create a new transaction based on the purchase order

            int index = VerifiedGood(purchase.Good);
            int adjustment = -1 * parcel.Transaction.Adjustment;
            int proposedAdjustment = purchase.Quantity * inventory[index].Worth;


            //If the quantity/value is correct
            if ( (index > -1) && (adjustment == proposedAdjustment) )
            {
                userTrans = parcel.Transaction;//The user transaction
                
                /*construct the merchant's transaction*/
                mercTrans.Account = account.Number;//The merchant's account number
                mercTrans.Authorizer = name; //Authorizer of the trans is merchant
                mercTrans.Requester = parcel.Transaction.Authorizer;//Same authorizer (not cert)
                mercTrans.Timestamp = parcel.Transaction.Timestamp; //same time as usertrans
                mercTrans.Adjustment = parcel.Transaction.Adjustment * -1;//The same value as trans inverted
                

                /* CREATE CERTIFICATE TO AUTHORIZE MERCHNAT TO BANK */
                Certificate bankCert = new Certificate(certificates[0]);
                bankCert.TimeStamp = parcel.Certificate.TimeStamp;
                bankCert.e_Transaction(mercTrans); //The merchant transaction encrypted

                /*construct the parcel*/
                //Purchase tempPurchase = parcel.d_Purchase();//temporary place for the purchaseDEPRECATED

                //Reencrypt everything with the bank/merchant certificate
                //Authorization remains untouched
                parcel.Certificate = bankCert; //Certificate Merchant/Bank
                parcel.Transaction = userTrans; //UserTransaction encrypted with M/B
                parcel.MerchantTransaction = mercTrans; //Merchant Transaction encrypted with M/B
                //parcel.e_Purchase(tempPurchase); //Purchase encrypted with M/B DEPRECATED

                
                parcel.Certificate.Token = null; //DELETE KEY!
            }
            else
            {
                parcel.MessageData = "Transaction amount is incorrect";
                //That means the user created the wrong transaction.
            }
            
            
            //So long as it's actually there..
            return parcel;
        }




        /// <summary>
        /// Registers a user into the merchant's database.
        /// </summary>
        /// <param name="parcel">A request from the user to be registered</param>
        /// <returns>A parcel that sends the shared key and approval.</returns>
        protected Parcel RegisterUser(Parcel parcel)
        {
            parcel.Type = "Registration Response";
            if (RetrieveCertificate(parcel.Sender, false) == null)
            {
                //First, if they are a merchant then add them to the list
                Certificate certificate = new Certificate();//create a new cert
                certificate.Token = new Token();//generate a key
                certificate.Issuer = name;//issue it
                certificate.Subject = parcel.Sender; //set the subject to the requester
                parcel.MessageData = "Approved"; //approve the request
                parcel.Receiver = parcel.Sender;//set the receiver as the requester
                parcel.Sender = name;//set self as sender
                parcel.Certificate = certificate; //set the certificate
                certificates.Add(certificate);//add the certificate to our database
            }
            return parcel;
        }

        /// <summary>
        /// Fulfills a user's order after the bank approves it
        /// </summary>
        /// <param name="parcel">Parcel containing the data of approval</param>
        /// <returns>A parcel of approval for the user</returns>
        protected Parcel CompleteUserOrder(Parcel parcel)
        {
            if (parcel.MessageData == "Approved")
            {
                parcel.Certificate = RetrieveCertificate(parcel.Sender, false);//get their certificate
                parcel.Sender = parcel.Receiver;//set self as sender
                parcel.Receiver = parcel.Source;
                //purchase is already in there, just return it.
            }
            else
            {
                parcel.MessageData = "User was not approved!";
            }

            return parcel;
        }


        /// <summary>
        /// The merchant's inventory.
        /// </summary>
        public BindingList<Good> Inventory
        {
            get { return inventory; }
            set { inventory = value; }
        }

    }
}
